package service

import (
	"fmt"
	"sync"

	"libvirt.org/go/libvirt"
)

// Connection wraps libvirt connection with thread-safe operations
type Connection struct {
	conn *libvirt.Connect
	mu   sync.RWMutex
	uri  string
}

// NewConnection creates a new libvirt connection
func NewConnection(uri string) (*Connection, error) {
	conn, err := libvirt.NewConnect(uri)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to libvirt at %s: %w", uri, err)
	}

	// Test connection
	if _, err := conn.GetVersion(); err != nil {
		conn.Close()
		return nil, fmt.Errorf("failed to verify libvirt connection: %w", err)
	}

	return &Connection{
		conn: conn,
		uri:  uri,
	}, nil
}

// Close closes the libvirt connection
func (c *Connection) Close() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.conn != nil {
		_, err := c.conn.Close()
		return err
	}
	return nil
}

// GetConnect returns the underlying libvirt connection
func (c *Connection) GetConnect() *libvirt.Connect {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.conn
}

// GetURI returns the connection URI
func (c *Connection) GetURI() string {
	return c.uri
}

// IsAlive checks if the connection is still alive
func (c *Connection) IsAlive() bool {
	c.mu.RLock()
	defer c.mu.RUnlock()

	if c.conn == nil {
		return false
	}

	alive, err := c.conn.IsAlive()
	return err == nil && alive
}

// Reconnect attempts to reconnect to libvirt
func (c *Connection) Reconnect() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.conn != nil {
		c.conn.Close()
	}

	conn, err := libvirt.NewConnect(c.uri)
	if err != nil {
		return fmt.Errorf("failed to reconnect to libvirt: %w", err)
	}

	c.conn = conn
	return nil
}

// GetCapabilities returns host capabilities
func (c *Connection) GetCapabilities() (string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	caps, err := c.conn.GetCapabilities()
	if err != nil {
		return "", fmt.Errorf("failed to get capabilities: %w", err)
	}
	return caps, nil
}

// GetHostname returns the hypervisor hostname
func (c *Connection) GetHostname() (string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	hostname, err := c.conn.GetHostname()
	if err != nil {
		return "", fmt.Errorf("failed to get hostname: %w", err)
	}
	return hostname, nil
}

// GetVersion returns libvirt version
func (c *Connection) GetVersion() (uint32, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	version, err := c.conn.GetVersion()
	if err != nil {
		return 0, fmt.Errorf("failed to get version: %w", err)
	}
	return version, nil
}
